//
//  SynchronizationObject.m
//
//  Created by Alexander Chaika on 24.01.12.
//  Copyright 2012 by Alexander Chaika. All rights reserved.
//

#import "resource.h"

#import "Synchronization.h"
#import "Synchronization+Contacts.h"
#import "Synchronization+Calendars.h"

@interface Synchronization (PrivateMethods)
- (void)getRefreshToken;
- (void)getAccessToken;
- (void)releaseCurrentConnection;
@end

enum OperationType {
	OperationType_None = 0, // no operation
	OperationType_RefreshToken, // get refresh token
	OperationType_AccessToken, // get access token
};

@implementation Synchronization

@synthesize accessToken;
@synthesize accessTokenExpires;
@synthesize contactsNextSync;
@synthesize calendarsNextSync;

- (id)init {
    self = [super init];
    if (self) {
		parser = [[SBJsonParser alloc] init];
    }    
    return self;
}

- (IBAction)synchronizeNow:(id)sender {
	[self syncContactsNow:sender];
	[self syncCalendarsNow:sender];
}

- (IBAction)syncContactsNow:(id)sender {
	[self updateContactsSyncScheduleWithInterval:0];
}

- (IBAction)syncCalendarsNow:(id)sender {
	[self updateCalendarsSyncScheduleWithInterval:0];
}

- (IBAction)updateSyncSchedule:(id)sender {
	[self updateContactsSyncSchedule:sender];
	[self updateCalendarsSyncSchedule:sender];
}

- (IBAction)updateContactsSyncSchedule:(id)sender {
	int scheduleInterval = [[NSUserDefaults standardUserDefaults]
							integerForKey:kSyncSchedule] * 60;
	[self updateContactsSyncScheduleWithInterval:scheduleInterval];
}

- (IBAction)updateCalendarsSyncSchedule:(id)sender {
	int scheduleInterval = [[NSUserDefaults standardUserDefaults]
							integerForKey:kSyncSchedule] * 60;
	[self updateCalendarsSyncScheduleWithInterval:scheduleInterval];
}

- (void)start {
	
	// renew data
	operationInProgress = OperationType_None;
	calendarsState = -1;
	contactsState = -1;
	
	// calculate next sync time
	[self updateSyncSchedule:nil];

	// TODO check if account setup is ok
	
	// start timer to check the update is needed
	[checkTimer release];
	checkTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0f
												   target:self
												 selector:@selector(check)
												 userInfo:nil
												  repeats:YES]
				  retain];
	[checkTimer fire];
}

- (void)stop {
	// stop check timer
	if ([checkTimer isValid])
		[checkTimer invalidate];
	
	// stop pending operations
	[curConnection cancel];
	[self releaseCurrentConnection];
}

- (void)restart {
	[self stop];
	[self start];
}

- (void)check {
	if (operationInProgress != OperationType_None) // do not do anything if operation pending
		return;
	
	// all tokens operation
	[self getRefreshToken]; // get refresh token here
	[self getAccessToken]; // get access token if there is no token
	
	// sync operations
	if (![[NSUserDefaults standardUserDefaults] boolForKey:kSyncStarted] &&
		contactsState < 0 && calendarsState < 0) // check if sync is started
		return;
	
	[self syncContacts]; // synchronization of contacts
}

#pragma mark Operations of getting tokens

- (void)getRefreshToken {
	if ([[NSUserDefaults standardUserDefaults] objectForKey:kRefreshToken])
		return;
	
	// get refresh token from the google
	NSString *code = [[NSUserDefaults standardUserDefaults] objectForKey:kAuthorizationCode]; // get code from defaults
	NSString *bodyData =[NSString stringWithFormat:@"code=%@&client_id=%@&client_secret=%@&redirect_uri=%@&grant_type=authorization_code",
						 code, client_id, client_secret, redirect_uri]; // prepare body data
	NSString *bodyLen = [NSString stringWithFormat:@"%d", [bodyData length]]; // body length
	
	// create request
	NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
	[request setURL:[NSURL URLWithString:@"https://accounts.google.com/o/oauth2/token"]];
	[request setHTTPMethod:@"POST"];
	[request setValue:bodyLen forHTTPHeaderField:@"Content-Length"];
	[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
	[request setHTTPBody:[bodyData dataUsingEncoding:NSUTF8StringEncoding]];
	
	// create connection
	if ((curConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self])) {
		receivedData = [[NSMutableData alloc] init];
		operationInProgress = OperationType_RefreshToken;
	}
}

- (void)getRefreshTokenDone {
    NSDictionary *object = [parser objectWithData:receivedData];
	if (object) {
		NSString *value = [object valueForKey:@"refresh_token"];
		if (value) {
			[[NSUserDefaults standardUserDefaults] setObject:value
													  forKey:kRefreshToken];
			return;
		}
    }

	// TODO error occured... need some rest
}

- (void)getAccessToken {
	NSString *refreshToken = [[NSUserDefaults standardUserDefaults]
							  objectForKey:kRefreshToken];
	if (!refreshToken) // need refresh token
		return;
	
	if (accessToken) { // already got access token
		if ([accessTokenExpires timeIntervalSinceNow] > 0.) // check access token timeout
			return;
		self.accessToken = nil;
	}
	
	// get access code from google
	NSString *bodyData =[NSString stringWithFormat:@"client_id=%@&client_secret=%@&refresh_token=%@&grant_type=refresh_token",
						 client_id, client_secret, refreshToken]; // prepare body data
	NSString *bodyLen = [NSString stringWithFormat:@"%d", [bodyData length]]; // body length
	
	// create request
	NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
	[request setURL:[NSURL URLWithString:@"https://accounts.google.com/o/oauth2/token"]];
	[request setHTTPMethod:@"POST"];
	[request setValue:bodyLen forHTTPHeaderField:@"Content-Length"];
	[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
	[request setHTTPBody:[bodyData dataUsingEncoding:NSUTF8StringEncoding]];
	
	// create connection
	if ((curConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self])) {
		receivedData = [[NSMutableData alloc] init];
		operationInProgress = OperationType_AccessToken;
	}
}

- (void)getAccessTokenDone {
    NSDictionary *object = [parser objectWithData:receivedData];
    if (object) {
		// get access token
		self.accessToken = [object valueForKey:@"access_token"];
		
		// parse the expires_in data to calculate the next time value receiving
		int expiresIn = [[object valueForKey:@"expires_in"] intValue] * 0.9; // it used to be 3600, but let's divide it to be sure it not expired
		self.accessTokenExpires = [NSDate dateWithTimeIntervalSinceNow:
								   (expiresIn <= 0) ? 1 : expiresIn];
		return;
    }
	
	// TODO error occured... need some rest
}

#pragma mark Internet connection and processing

- (void)releaseCurrentConnection {
	operationInProgress = OperationType_None;
	
    [curConnection release];
	[receivedData release];
	curConnection = nil;
	receivedData = nil;
	
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
	[receivedData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
	[receivedData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
	NSLog(@"Connection failed! Error - %@ %@", [error localizedDescription],
		  [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
	[self releaseCurrentConnection];
	
	// TODO error occured... need some rest
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
	
	// process received data
	if (operationInProgress == OperationType_RefreshToken) // refresh token
		[self getRefreshTokenDone];
	else if (operationInProgress == OperationType_AccessToken) // access token
		[self getAccessTokenDone];
	else {
		NSLog(@"%@", [[[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding] autorelease]);
	}
	[self releaseCurrentConnection];
}

#pragma mark Release

- (void)dealloc {
	[self releaseCurrentConnection];
	self.contactsNextSync = nil;
	self.calendarsNextSync = nil;
	self.accessTokenExpires = nil;
	self.accessToken = nil;
    [checkTimer release];
	[parser release];
    [super dealloc];
}

@end
